1
|
|
|
"use strict"; |
2
|
|
|
|
3
|
|
|
/* ------------------------------------------------------------------------ */ |
4
|
|
|
|
5
|
|
|
const O = Object, |
|
|
|
|
6
|
|
|
isBrowser = (typeof window !== 'undefined') && (window.window === window) && window.navigator, |
7
|
|
|
SourceMapConsumer = require ('source-map').SourceMapConsumer, |
8
|
|
|
path = require ('./impl/path'), |
9
|
|
|
dataURIToBuffer = require ('data-uri-to-buffer'), |
10
|
|
|
lastOf = x => x[x.length - 1] |
|
|
|
|
11
|
|
|
|
12
|
|
|
/* ------------------------------------------------------------------------ */ |
13
|
|
|
|
14
|
|
|
const memoize = f => { |
15
|
|
|
|
16
|
|
|
const m = x => (x in m.cache) ? m.cache[x] : (m.cache[x] = f(x)) |
17
|
|
|
m.forgetEverything = () => { m.cache = Object.create (null) } |
18
|
|
|
m.cache = Object.create (null) |
19
|
|
|
|
20
|
|
|
return m |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
/* ------------------------------------------------------------------------ */ |
24
|
|
|
|
25
|
|
|
const newSourceFileMemoized = memoize (file => new SourceFile (file)) |
26
|
|
|
|
27
|
|
|
const getSource = module.exports = file => { return newSourceFileMemoized (path.resolve (file)) } |
28
|
|
|
|
29
|
|
|
getSource.resetCache = () => newSourceFileMemoized.forgetEverything () |
30
|
|
|
getSource.getCache = () => newSourceFileMemoized.cache |
31
|
|
|
|
32
|
|
|
/* ------------------------------------------------------------------------ */ |
33
|
|
|
|
34
|
|
|
class SourceMap { |
35
|
|
|
|
36
|
|
|
constructor (originalFilePath, sourceMapPath) { |
37
|
|
|
|
38
|
|
|
this.file = sourceMapPath.startsWith ('data:') |
39
|
|
|
? new SourceFile (originalFilePath, dataURIToBuffer (sourceMapPath).toString ()) |
40
|
|
|
: getSource (path.relativeToFile (originalFilePath, sourceMapPath)) |
41
|
|
|
|
42
|
|
|
this.parsed = (this.file.text && SourceMapConsumer (JSON.parse (this.file.text))) || null |
43
|
|
|
this.sourceFor = memoize (this.sourceFor.bind (this)) |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
sourceFor (file) { |
47
|
|
|
const content = this.parsed.sourceContentFor (file, true /* return null on missing */) |
48
|
|
|
const fullPath = path.relativeToFile (this.file.path, file) |
49
|
|
|
return content ? new SourceFile (fullPath, content) : getSource (fullPath) |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
resolve (loc) { |
53
|
|
|
|
54
|
|
|
const originalLoc = this.parsed.originalPositionFor (loc) |
55
|
|
|
return originalLoc.source ? this.sourceFor (originalLoc.source) |
56
|
|
|
.resolve (O.assign ({}, loc, { |
57
|
|
|
line: originalLoc.line, |
58
|
|
|
column: originalLoc.column, |
59
|
|
|
name: originalLoc.name })) : loc |
60
|
|
|
} |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/* ------------------------------------------------------------------------ */ |
64
|
|
|
|
65
|
|
|
class SourceFile { |
66
|
|
|
|
67
|
|
|
constructor (path, text /* optional */) { |
68
|
|
|
|
69
|
|
|
this.path = path |
70
|
|
|
|
71
|
|
|
if (text) { |
72
|
|
|
this.text = text } |
73
|
|
|
|
74
|
|
|
else { |
75
|
|
|
try { |
76
|
|
|
if (isBrowser) { |
77
|
|
|
|
78
|
|
|
let xhr = new XMLHttpRequest () |
|
|
|
|
79
|
|
|
|
80
|
|
|
xhr.open ('GET', path, false /* SYNCHRONOUS XHR FTW :) */) |
81
|
|
|
xhr.send (null) |
82
|
|
|
|
83
|
|
|
this.text = xhr.responseText } |
84
|
|
|
|
85
|
|
|
else { |
86
|
|
|
this.text = module.require ('fs').readFileSync (path, { encoding: 'utf8' }) } } |
87
|
|
|
|
88
|
|
|
catch (e) { |
89
|
|
|
this.error = e |
90
|
|
|
this.text = '' } } |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
get lines () { |
94
|
|
|
return (this.lines_ = this.lines_ || this.text.split ('\n')) |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
get sourceMap () { |
98
|
|
|
|
99
|
|
|
try { |
100
|
|
|
|
101
|
|
|
if (this.sourceMap_ === undefined) { |
102
|
|
|
|
103
|
|
|
// Node v4 does not support destructuring... |
104
|
|
|
// const [,url] = this.text.match (/\u0023 sourceMappingURL=(.+)\n?/) || [undefined, undefined] // escape #, otherwise it will match this exact line.. %) |
105
|
|
|
|
106
|
|
|
const match = this.text.match (/\u0023 sourceMappingURL=(.+)\n?/) || [undefined, undefined] // escape #, otherwise it will match this exact line.. %) |
107
|
|
|
, url = match[1] |
108
|
|
|
|
109
|
|
|
if (url) { |
110
|
|
|
|
111
|
|
|
const sourceMap = new SourceMap (this.path, url) |
112
|
|
|
|
113
|
|
|
if (sourceMap.parsed) { |
114
|
|
|
this.sourceMap_ = sourceMap |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
} else { |
118
|
|
|
|
119
|
|
|
this.sourceMap_ = null |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
catch (e) { |
125
|
|
|
this.sourceMap_ = null |
126
|
|
|
this.sourceMapError = e |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
return this.sourceMap_ |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
resolve (loc /* { line[, column] } */) /* → { line, column, sourceFile, sourceLine } */ { |
133
|
|
|
|
134
|
|
|
return this.sourceMap ? this.sourceMap.resolve (loc) : O.assign ({}, loc, { |
135
|
|
|
|
136
|
|
|
sourceFile: this, |
137
|
|
|
sourceLine: (this.lines[loc.line - 1] || ''), |
138
|
|
|
error: this.error |
139
|
|
|
}) |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/* ------------------------------------------------------------------------ */ |
144
|
|
|
|